# -*- coding: utf-8 -*-

from __future__ import with_statement

from unittest import TestCase
from mock import Mock

from flask import session, g
from dicthash.utils import Bunch
from dicthash.for_testing import TestFlask, captured_templates

from auth.code.views import _login_handler, _create_user_handler
from auth.code import _set_user
from auth.code import config


class MyFlask(TestFlask):
    """ """
    TEMPLATES = {
        'auth/create_user.html': """ 
            %auth/create_user.html%
        """
    }


class TestBase(TestCase):
    """ """
    def setUp(self):
        reload(config)
        self.app = MyFlask(__name__)
        self.app.config.update(
            SECRET_KEY = '12345'
        )
        
        
class TestLoginHandler(TestBase):
    """ """
    def test_login_handler_user_exists(self):
        """ """
        User = Mock()
        User.get_by_openid = Mock(return_value=Bunch(name='xoxo', openid='foo@gmail.com'))
        
        with self.app.test_request_context('/login'):
            rv = _login_handler(User, 'foo@gmail.com', 'goto_url')
            
            self.assertEqual(rv.status_code, 302)
            self.assertEqual(session['user'], dict(name='xoxo', openid='foo@gmail.com'))
            
    def test_login_handler_user_not_exists(self):
        """ """
        User = Mock()
        User.get_by_openid = Mock(return_value=None)
        
        with self.app.test_request_context('/login'):
            with captured_templates(self.app) as tmpl_info:
                _login_handler(User, 'foo@gmail.com', 'goto_url')
                
                self.assertEqual(tmpl_info[0][0], "auth/create_user.html")
                self.assertTrue('next' in tmpl_info[0][1])
                self.assertEqual(tmpl_info[0][1]['next'], "goto_url")
            
            
class TestCreateUser(TestBase):
    """ """
    def setUp(self):
        super(TestCreateUser, self).setUp()
        self.validator = lambda name: (name, None) if name else (None, 'name is blank')
                
    def test_name_valid(self):
        """ """
        User = Mock()
        usr = Mock(); usr.name = 'foo'; usr.openid = 'foo@gmail.com'
        User.new = Mock(return_value=usr)
        
        with self.app.test_request_context('/create'):
            rv = _create_user_handler(name= 'foo',
                                      google_user_openid= 'foo@gmail.com',
                                      next= '/goto_url',
                                      validator= self.validator,
                                      user_class= User)
            
            self.assertEqual(rv.status_code, 302)
            self.assertEqual(session['user'], dict(name='foo', openid='foo@gmail.com'))
            
    def test_name_invalid(self):
        """ """
        User = Mock()
                
        with self.app.test_request_context('/create'):
            with captured_templates(self.app) as tmpl_info:
                _create_user_handler(name= '',
                                     google_user_openid= 'foo@gmail.com',
                                     next= '/goto_url',
                                     validator= self.validator,
                                     user_class= User)
                
                self.assertEqual(tmpl_info[0][0], "auth/create_user.html")
                self.assertTrue('name' in tmpl_info[0][1])
                self.assertTrue('error' in tmpl_info[0][1])
                self.assertEqual(tmpl_info[0][1]['name'], '')
                self.assertEqual(tmpl_info[0][1]['error'], 'name is blank')
                
    def test_name_valid_user_error(self):
        """
        Name is valid but excetion is raised in `put` method of `User` class
        """
        class UserError(Exception):
            pass
        def make_error():
            raise UserError
        
        User = Mock()
        usr = Mock()
        usr.name = 'foo'; usr.openid = 'foo@gmail.com'
        usr.put = Mock(side_effect=make_error)
        User.new = Mock(return_value=usr)
        
        with self.app.test_request_context('/create'):
            with captured_templates(self.app) as tmpl_info:
                _create_user_handler(name= 'foo',
                                     google_user_openid= 'foo@gmail.com',
                                     next= '/goto_url',
                                     validator= self.validator,
                                     user_class= User,
                                     user_errors= {UserError: "fucking error"})
                
                self.assertEqual(tmpl_info[0][0], "auth/create_user.html")
                self.assertTrue('name' in tmpl_info[0][1])
                self.assertTrue('error' in tmpl_info[0][1])
                self.assertEqual(tmpl_info[0][1]['name'], 'foo')
                self.assertEqual(tmpl_info[0][1]['error'], "fucking error")
                
    def test_name_valid_db_error(self):
        """
        Name is valid but excetion is raised in `put` method of `User` class
        """
        class DataBaseError(Exception):
            pass
        def make_error():
            raise DataBaseError
        
        User = Mock()
        usr = Mock()
        usr.name = 'foo'; usr.openid = 'foo@gmail.com'
        usr.put = Mock(side_effect=make_error)
        User.new = Mock(return_value=usr)
        
        with self.app.test_request_context('/create'):
            with captured_templates(self.app) as tmpl_info:
                try:
                    _create_user_handler(name= 'foo',
                                         google_user_openid= 'foo@gmail.com',
                                         next= '/goto_url',
                                         validator= self.validator,
                                         user_class= User,
                                         user_errors= {})
                except DataBaseError:
                    pass    
                
                self.assertEqual(len(tmpl_info), 0)
                        
                  
class TestSetUser(TestBase):
    """ """
    def test_google_anonym_app_anonym(self):
        """
        User is not logged both in Google and application.
        """
        with self.app.test_request_context('/'):
            self.assertTrue('user' not in session)
            g.user = None
            _set_user(None)
            self.assertTrue('user' not in session)
            self.assertTrue(g.user is None)
            
    def test_google_anonym_app_logged(self):
        """
        User is logged in Google but not in application. Application session must be deleted.
        """
        with self.app.test_request_context('/'):
            session['user'] = {
                'name': 'foo',
                'openid': 'foo@gmail.com'
            }
            g.user = None
            _set_user(None)
            self.assertTrue('user' not in session)
            self.assertTrue(g.user is None)      
            
    def test_google_logged_app_anonym(self):
        """
        User is logged in Google but not in application.
        """
        with self.app.test_request_context('/'):
            self.assertTrue('user' not in session)
            g.user = None       
            _set_user(True)
            self.assertTrue('user' not in session)
            self.assertTrue(g.user is None)
            
    def test_google_logged_app_logged_session_corrupted(self):
        """
        User is logged both in Google and application. But session is corrupted.
        Check that session is cleared.
        """
        with self.app.test_request_context('/'):
            session['user'] = {
                'name_': 'foo',
                'openid': 'foo@gmail.com'
            }
            g.user = None
            self.assertTrue('user' in session)  
            _set_user(True)
            self.assertTrue('user' not in session)
            self.assertTrue(g.user is None)
            
    def test_google_logged_app_logged_openid_not_match(self):
        """
        User is logged both in Google and application. Openid of google user did not match
        openid in session.
        """
        with self.app.test_request_context('/'):
            session['user'] = {
                'name': 'foo',
                'openid': 'foo@gmail.com'
            }
            g.user = None
            self.assertTrue('user' in session)   
            _set_user(Bunch(openid='bar@gmail.com'))
            self.assertTrue('user' not in session)
            self.assertTrue(g.user is None)
            
    def test_google_logged_app_logged_openid_match(self):
        """
        User is logged both in Google and application. Openid of google user did match
        openid in session.
        """
        with self.app.test_request_context('/'):
            session['user'] = {
                'name': 'foo',
                'openid': 'foo@gmail.com'
            }
            g.user = None
            self.assertTrue('user' in session)   
            _set_user(Bunch(openid='foo@gmail.com'))
            self.assertTrue('user' in session)
            self.assertTrue(g.user is not None)   
            self.assertEqual(g.user.name, 'foo')
            self.assertEqual(g.user.openid, 'foo@gmail.com')      